#include "Renderer.h"
#include "CullNodeVisitor.h"
#pragma comment( lib, "opengl32.lib")
#pragma comment( lib, "glu32.lib")
namespace sl
{
	Renderer* Renderer::ms_pRenderer = NULL;

	/************************************************************************/
	Renderer::~Renderer(void)
	{
	}

	/************************************************************************/
	Renderer::Renderer(void)
	{
		
	}

	/************************************************************************/
	/*                                                                      */
	/************************************************************************/
	Renderer * Renderer::Create( HWND hWnd, bool bFullScreen )
	{
		ms_pRenderer = new Renderer();

		if ( !ms_pRenderer->Init(hWnd, bFullScreen ) )
		{
			ms_pRenderer->ShutDown();
			return 0;
		}
		
		return ms_pRenderer;
	}

	/************************************************************************/
	/* initialize the opengl device                                                                     */
	/************************************************************************/
	void	Renderer::Destroy( Renderer* pRenderer )
	{
		pRenderer->ShutDown();
	}

	/************************************************************************/
	/* initialize the opengl device                                                                     */
	/************************************************************************/
	bool Renderer::Init( HWND hWnd, bool bFullScreen )
	{

		if ( hWnd == INVALID_HANDLE_VALUE )
			return false;

		m_hWnd = hWnd;
		m_bFullScreen = bFullScreen;
		if ( m_bFullScreen )
		{
		
			EnumDisplaySettings(NULL,ENUM_CURRENT_SETTINGS, &m_oldDevMode);
			int nMode = 0;
			bool bDesiredDevModeFound = false;
			while ( EnumDisplaySettings( NULL, nMode++,&m_curDevMode ) )
			{
				if( m_curDevMode.dmPelsWidth  != 640 || m_curDevMode.dmPelsHeight != 480)
					continue;

				if( m_curDevMode.dmBitsPerPel != 32 )
					continue;

				if( m_curDevMode.dmDisplayFrequency != 75 )
					continue;

				if( ChangeDisplaySettings( &m_curDevMode, CDS_TEST ) == DISP_CHANGE_SUCCESSFUL )
				{
					bDesiredDevModeFound = true;
					break;
				}
			}

			if ( !bDesiredDevModeFound )
			{
				return false;
			}


			m_hDC = GetDC( m_hWnd );

			PIXELFORMATDESCRIPTOR pfd;

			int nTotalFormats = DescribePixelFormat( m_hDC, 1, sizeof(pfd), NULL );

			int nPixelFormat;

			for( nPixelFormat = 1; nPixelFormat <= nTotalFormats; ++nPixelFormat )
			{
				if( DescribePixelFormat( m_hDC, nPixelFormat, sizeof(pfd), &pfd ) == 0 )
				{
					DWORD dwErrorCode = GetLastError();
					return false;
				}

				if( !(pfd.dwFlags & PFD_SUPPORT_OPENGL) )
					continue;

				if( !(pfd.dwFlags & PFD_DOUBLEBUFFER) )
					continue;

				if( pfd.iPixelType != PFD_TYPE_RGBA )
					continue;

				if( pfd.cColorBits != 32 )
					continue;

				if( pfd.cDepthBits != 16 )
					continue;

				break;
			}
	
			if( SetPixelFormat( m_hDC, nPixelFormat, &pfd) == FALSE )
			{
				DWORD dwErrorCode = GetLastError();
				return false;
			}

		}
		else
		{
			PIXELFORMATDESCRIPTOR pfd;
			memset( &pfd, 0, sizeof(PIXELFORMATDESCRIPTOR) );

			pfd.nSize		= sizeof(PIXELFORMATDESCRIPTOR);
			pfd.nVersion	= 1.0;
			pfd.dwFlags		= PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER; 
			pfd.iPixelType	= PFD_TYPE_RGBA;
			pfd.cColorBits	= 16;
			pfd.cDepthBits	= 16;

			m_hDC = GetDC( m_hWnd );

			u32 iPixelFormat = ChoosePixelFormat( m_hDC, &pfd );

			if ( iPixelFormat != 0 )
			{
				PIXELFORMATDESCRIPTOR bestMatch_pfd;
				DescribePixelFormat( m_hDC, iPixelFormat, sizeof(PIXELFORMATDESCRIPTOR), &bestMatch_pfd);
				if ( bestMatch_pfd.cDepthBits < pfd.cDepthBits )
				{
					return false;
				}

				if ( SetPixelFormat( m_hDC, iPixelFormat, &pfd ) == false )
				{
					DWORD dwErrorCode = GetLastError();
					return false;
				}

			}
			else
			{
				DWORD dwErrorCode = GetLastError();
				return false;
			}

		}

		m_hRC = wglCreateContext( m_hDC );

		wglMakeCurrent( m_hDC, m_hRC );

		glClearColor( 0.0f, 0.0f, 0.0f, 1.0f );

		glMatrixMode( GL_PROJECTION );
		glLoadIdentity();
		gluPerspective( 45.0f, 1024 / 768.0f, 0.1f, 100.0f);

		glCullFace( GL_BACK );
		glEnable( GL_CULL_FACE );

		if ( m_bFullScreen )
		{
			if ( ChangeDisplaySettings( &m_curDevMode, CDS_FULLSCREEN ) != DISP_CHANGE_SUCCESSFUL )
			{
				DWORD dwErrorCode = GetLastError();
				return false;
			}
		}

		return true;

	}

	void Renderer::ShutDown()
	{
		// back to the old device mode
		if ( m_bFullScreen )
		{
			ChangeDisplaySettings( &m_oldDevMode, CDS_UPDATEREGISTRY );
		}

		if ( m_hRC )
		{
			wglMakeCurrent( NULL, NULL );
			wglDeleteContext( m_hRC );
			m_hRC = NULL;
		}

		if ( m_hDC )
		{
			ReleaseDC( m_hWnd, m_hDC );
			m_hDC = NULL;
		}
	}

	void Renderer::RenderScene( sl::Camera * pCamera, sl::Node * pNodeData )
	{

		PreRenderFrame();
		if ( !m_rSceneView )
		{
			m_rSceneView = new SceneView();
		}
	
		m_rSceneView->Render( pCamera, pNodeData );
		// output the error information: the camera is not set sucessfull
	

		// update the all scene node : remove the old node and add the new node
		

		// culling with the node visitor
		// draw
		// get the camera infor
		// set the renderinfo data
		RenderFrame();

		PostRenderFrame();

	}

	void Renderer::RenderFrame()
	{
		GLenum g_currentPrimitive = GL_POLYGON;

		struct Vertex
		{
			unsigned char r, g, b, a;
			float x, y, z;
		};

		Vertex g_points[] = 
		{
			{ 255,   0,   0, 255,  0.0f, 0.0f, 0.0f },
			{   0, 255,   0, 255,  0.5f, 0.0f, 0.0f },
			{   0,   0, 255, 255, -0.5f, 0.0f, 0.0f },
			{ 255, 255,   0, 255,  0.0f,-0.5f, 0.0f },
			{ 255,   0, 255, 255,  0.0f, 0.5f, 0.0f }
		};

		Vertex g_lines[] = 
		{
			{ 255,   0,   0, 255, -1.0f, 0.0f, 0.0f },  // Line #1
			{ 255,   0,   0, 255,  0.0f, 1.0f, 0.0f },

			{   0, 255,   0, 255,  0.5f, 1.0f, 0.0f },  // Line #2
			{   0, 255,   0, 255,  0.5f,-1.0f, 0.0f },

			{   0,   0, 255, 255,  1.0f, -0.5f, 0.0f }, // Line #3
			{   0,   0, 255, 255, -1.0f, -0.5f, 0.0f }
		};

		Vertex g_lineStrip_and_lineLoop[] = 
		{
			{ 255,   0,   0, 255,  0.5f, 0.5f, 0.0f },
			{   0, 255,   0, 255,  1.0f, 0.0f, 0.0f },
			{   0,   0, 255, 255,  0.0f,-1.0f, 0.0f },
			{ 255, 255,   0, 255, -1.0f, 0.0f, 0.0f },
			{ 255,   0,   0, 255,  0.0f, 0.0f, 0.0f },
			{ 255,   0, 255, 255,  0.0f, 1.0f, 0.0f }
		};

		Vertex g_triangles[] =
		{
			{ 255,   0,   0, 255, -1.0f, 0.0f, 0.0f }, // Triangle #1
			{   0,   0, 255, 255,  1.0f, 0.0f, 0.0f },
			{   0, 255,   0, 255,  0.0f, 1.0f, 0.0f },


			{ 255, 255,   0, 255, -0.5f,-1.0f, 0.0f }, // Triangle #2
			{ 255,   0,   0, 255,  0.5f,-1.0f, 0.0f },
			{   0, 255, 255, 255,  0.0f,-0.5f, 0.0f }
		};

		Vertex g_triangleStrip[] = 
		{
			{ 255,   0,   0, 255, -2.0f, 0.0f, 0.0f },
			{   0,   0, 255, 255, -1.0f, 0.0f, 0.0f },	
			{   0, 255,   0, 255, -1.0f, 1.0f, 0.0f },
			{ 255,   0, 255, 255,  0.0f, 0.0f, 0.0f },
			{ 255, 255,   0, 255,  0.0f, 1.0f, 0.0f },
			{ 255,   0,   0, 255,  1.0f, 0.0f, 0.0f },
			{   0, 255, 255, 255,  1.0f, 1.0f, 0.0f },
			{   0, 255,   0, 255,  2.0f, 1.0f, 0.0f }
		};

		Vertex g_triangleFan[] = 
		{
			{ 255,   0,   0, 255,  0.0f,-1.0f, 0.0f },
			{   0, 255, 255, 255,  1.0f, 0.0f, 0.0f },
			{ 255,   0, 255, 255,  0.5f, 0.5f, 0.0f },
			{ 255, 255,   0, 255,  0.0f, 1.0f, 0.0f },
			{   0,   0, 255, 255, -0.5f, 0.5f, 0.0f },
			{   0, 255,   0, 255, -1.0f, 0.0f, 0.0f }
		};

		Vertex g_quads[] =
		{
			{ 255,   0,   0, 255,  -0.5f,-0.5f, 0.0f },  // Quad #1
			{   0, 255,   0, 255,   0.5f,-0.5f, 0.0f },
			{   0,   0, 255, 255,   0.5f, 0.5f, 0.0f },
			{ 255, 255,   0, 255,  -0.5f, 0.5f, 0.0f },

			{ 255,   0, 255, 255,  -1.5f, -1.0f, 0.0f }, // Quad #2
			{   0, 255, 255, 255,  -1.0f, -1.0f, 0.0f },
			{ 255,   0,   0, 255,  -1.0f,  1.5f, 0.0f },
			{   0, 255,   0, 255,  -1.5f,  1.5f, 0.0f },

			{   0,   0, 255, 255,  1.0f, -0.2f, 0.0f },  // Quad #3
			{ 255, 255,   0, 255,  2.0f, -0.2f, 0.0f },
			{   0, 255, 255, 255,  2.0f,  0.2f, 0.0f },
			{ 255,   0, 255, 255,  1.0f,  0.2f, 0.0f }
		};

		Vertex g_quadStrip[] =
		{
			{ 255,   0,   0, 255,  -0.5f,-1.5f, 0.0f },
			{   0, 255,   0, 255,   0.5f,-1.5f, 0.0f },
			{   0,   0, 255, 255,  -0.2f,-0.5f, 0.0f },
			{ 255, 255,   0, 255,   0.2f,-0.5f, 0.0f },
			{ 255,   0, 255, 255,  -0.5f, 0.5f, 0.0f },
			{   0, 255, 255, 255,   0.5f, 0.5f, 0.0f },
			{ 255,   0,   0, 255,  -0.4f, 1.5f, 0.0f },
			{   0, 255,   0, 255,   0.4f, 1.5f, 0.0f },
		};

		Vertex g_polygon[] =
		{
			{ 255,   0,   0, 255,  -0.3f,-1.5f, 0.0f },
			{   0, 255,   0, 255,   0.3f,-1.5f, 0.0f },
			{   0,   0, 255, 255,   0.5f, 0.5f, 0.0f },
			{ 255, 255,   0, 255,   0.0f, 1.5f, 0.0f },
			{ 255,   0, 255, 255,  -0.5f, 0.5f, 0.0f }
		};

		glMatrixMode (GL_PROJECTION);					
		glLoadIdentity ();					
		gluPerspective( 45.0, (GLfloat)(1024)/(GLfloat)(768), 0.1f, 500.0 );
		glMatrixMode(GL_MODELVIEW);
		glLoadIdentity();
		glMatrixMode( GL_MODELVIEW );
		glLoadIdentity();
		glTranslatef( 0.0f, 0.0f, -5.0f );

	
		glInterleavedArrays( GL_C4UB_V3F, 0, g_points );
		glDrawArrays( GL_POINTS, 0, 5 );

		glInterleavedArrays( GL_C4UB_V3F, 0, g_lines );
		glDrawArrays( GL_LINES, 0, 6 );

		
		glInterleavedArrays( GL_C4UB_V3F, 0, g_quadStrip );
		glDrawArrays( GL_QUAD_STRIP, 0, 8 );
		

		glInterleavedArrays( GL_C4UB_V3F, 0, g_polygon );
		glDrawArrays( GL_POLYGON, 0, 5 );

		SwapBuffers( m_hDC );
	}	

	void Renderer::PostRenderFrame()
	{

	}

	void Renderer::PreRenderFrame()
	{

	}
}
